package com.ztp.lib.files;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import org.apache.commons.io.FilenameUtils;


public class LocalExplorer implements DisplayableExplorer {
	public static final String TAG = "DirExplorer::";
	
	public static final File BACK = new File("..");
	ArrayList<File> files = new ArrayList<File>();
	private File currentDir;
	private File oldDir;
	private DisplayableFile lastRoot = null;
	private HashMap <DisplayableFile, File> lastDirectoriesInRoots = new HashMap<DisplayableFile, File>();
	
	public LocalExplorer(){
		File [] roots = File.listRoots();
		if(roots != null && roots.length > 0){
			initialise(roots[0]);
		}
		else{
			initialise(new File(""));
		}
	}
	
	public LocalExplorer(String startDirectory){
		this(new File(startDirectory));
	}
	
	public LocalExplorer(File startDirectory){
		initialise(startDirectory);
	}
	private void initialise(File startDirectory){
		DisplayableFile [] roots = getRoots();
		for(DisplayableFile r : roots){
			lastDirectoriesInRoots.put(r, new File(r.absPath));
		}
		
		setCurrentDir(startDirectory);
		applyRootChanged();
		listCurrentDir();
	}
	
	///------------PRIVATE METHODS
	
	private void setCurrentDir(File dir){
		if(dir == null)	throw new NullPointerException(TAG +"setCurrentDir():  dir == null");
		if(!dir.exists()) throw new IllegalArgumentException(TAG +"setCurrentDir(File dir) startDirectory does not exists!");
		if(!dir.isDirectory()) throw new IllegalArgumentException(TAG +"setCurrentDir(File dir) !dir.isDirectory()");
		oldDir = (currentDir == null)? dir : currentDir;
		
		if(dir == BACK ){
			if(currentDir == null) return;
			File parent = currentDir.getAbsoluteFile().getParentFile();
			currentDir = parent;
			/*if(parent != null){
				currentDir = parent;
			}
			else{
				currentDir = null;
			}*/
		}
		else{
			currentDir = dir;
		}
	}
	
	private void listCurrentDir() throws IllegalStateException{
		File [] list = null;
		if(currentDir == null){
			list = File.listRoots();
		}
		else{
			list = currentDir.listFiles();
		}
		
		if(list == null) throw new IllegalStateException(TAG +"listCurrentDir(): Cannot access file or directory. ");
		
		if(!files.isEmpty()){
			files.clear();
		}
		
		files.add(BACK);
		for(int i=0;i < list.length;i++){
			files.add(list[i]);
		}
		
		applyDirectoryChanged(currentDir);
	}
	
	//-------------PUBLIC METHODS
	public String getCurrDirAbsPath(){
		return (currentDir != null)?currentDir.getAbsolutePath():DisplayableFile.ROOT.absPath;
	}
	
	public File get(int index){
		return files.get(index);
	}
	
	public static File [] listFiles(String dir){
		//TODO 
		return null;
	}
	
	public static File [] listFiles(File dir){
		//TODO 
		return null;
	}
	
	public static void listFiles(ArrayList<File> dirFiles, int position){
		if(dirFiles == null || dirFiles.isEmpty()){
			throw new IllegalArgumentException(TAG+"listFiles(ArrayList<File> dirFiles, int position)" +
					": dirFiles null or empty!");
		}
		if(position < 0 || position >= dirFiles.size()){
			throw new IndexOutOfBoundsException(TAG+"listFiles(ArrayList<File> dirFiles, int position)" +
					": position is out of range!");
		}
		
		File dir = dirFiles.get(position);
		if(!dir.isDirectory()){
			throw new IllegalStateException(TAG+"listFiles(ArrayList<File> dirFiles, int position)" +
					": file is not directory!");
		}
		
		File [] fileList = dir.listFiles();
		dirFiles.clear();
		for(int i=0;i<fileList.length;i++){
			dirFiles.add(fileList[i]);
		}
	}
	
	public static void listFiles(ArrayList<File> dirFiles, File file) throws IllegalStateException{
		if((dirFiles == null || dirFiles.isEmpty()) && file == null){
			throw new IllegalArgumentException(TAG+"listFiles(ArrayList<File> dirFiles, File file)" +
					"\n: file is null!");
		}
		if(!file.isDirectory()){
			throw new IllegalStateException(TAG+"listFiles(ArrayList<File> dirFiles, File file)" +
					"\n: file is not directory!");
		}
		
		
		File [] fileList = file.listFiles();
		if(fileList == null){
			throw new IllegalStateException(TAG+"listFiles(ArrayList<File> dirFiles, File file) - Cannot access file or directory. ");
		}
		
		if(!dirFiles.isEmpty()){
			dirFiles.clear();
		}
		
		dirFiles.add(new File(".."));
		
		for(int i=0;i<fileList.length;i++){
			dirFiles.add(fileList[i]);
		}
	}
	
	///------------LISTENERS
	
	HashSet<DirectoryChangeListener> dirChangeListeners = new HashSet<DirectoryChangeListener>();
	
	private void applyDirectoryChanged( File currDir){
		Iterator<DirectoryChangeListener> it =  dirChangeListeners.iterator();
		DisplayableFile curr = (currDir == null)?null:new DisplayableFile(currDir);
		while(it.hasNext()){
			it.next().directoryChanged(curr);
		}
	}
	
	private void applyRootChanged(){
		DisplayableFile dRoot = null;
		if(currentDir == null){
			dRoot = DisplayableFile.ROOT;
		}
		else{
			try{
				dRoot = new DisplayableFile(currentDir.toPath().getRoot().toFile());
			}
			catch(NullPointerException e){}
			
			if(dRoot == null)
				throw new NullPointerException(TAG + "applyRootChanged() newRoot == null");
			
			if(lastRoot != null && lastRoot.equalPaths(dRoot) )
				return ;
		}
		 
		lastRoot = dRoot;
		Iterator<DirectoryChangeListener> it =  dirChangeListeners.iterator();
		while(it.hasNext()){
			it.next().rootChanged(dRoot);
		}
	}
	
	///------------DisplayableExplorer 
	
	@Override
	public void removeDirectoryChangeListener(DirectoryChangeListener dcl) {
		this.dirChangeListeners.remove(dcl);
	}
	
	@Override
	public void addDirectoryChangeListener(DirectoryChangeListener dcl) {
		this.dirChangeListeners.add(dcl);
	}

	@Override
	public DisplayableFile getDisplayableFile(int index) {
		return new DisplayableFile(files.get(index));
	}

	@Override
	public int getSize() {
		return files.size();
	}

	@Override
	public boolean setDirectory(int index) {
		File f = files.get(index);
		if(!f.isDirectory()){return false;}
		setCurrentDir(f);
		applyRootChanged();
		listCurrentDir();
		return true;
	}

	@Override
	public String getCurrentDirName() {
		if(currentDir == null) return "";
		return currentDir.getName();
	}

	@Override
	public String getCurrentDirAbsPath() {
		if(currentDir == null) return "";
		return currentDir.getAbsolutePath();
	}

	@Override
	public void getFile(int index, DisplayableExplorer explorer) {
		//TODO 
	}
	
	@Override
	public boolean tryToChangeDirectory(String path) throws Exception{
		File file = new File(path);
		if(file.exists() && file.isDirectory()){
			setCurrentDir(file);
			applyRootChanged();
			listCurrentDir();
			return true;
		}
		return false;
	}

	@Override
	public DisplayableFile[] getRoots() {
		File  [] roots = File.listRoots();
		DisplayableFile [] dFiles = null;
		if(roots != null){
			dFiles = new DisplayableFile [roots.length + 1];
			int i=0;
			for(i=0;i<roots.length;i++){
				dFiles[i] = new DisplayableFile(roots[i]);
			}
			dFiles[i] =  DisplayableFile.ROOT;
		}
		return dFiles;
	}

	@Override
	public boolean setUpDirectory() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void changeRoot(DisplayableFile root) throws NullPointerException, IllegalStateException {
		if(root == null || root.absPath == null) throw new NullPointerException(TAG + "changeRoot(DisplayableFile root) - root == null");
		setCurrentDir(new File(root.absPath));
		listCurrentDir();
	}

	@Override
	public String[] getRootsPath() {
		File [] roots = File.listRoots();
		String [] rootsPath = null;
		if(roots != null){
			rootsPath = new String[roots.length +1];
			int i=0;
			for( i=0;i<roots.length;i++){
				rootsPath[i] = roots[i].getAbsolutePath();
			}
			rootsPath[i] = DisplayableFile.ROOT.absPath;
		}
		return rootsPath;
	}

	@Override
	public void changeRoot(String rootPath) throws NullPointerException, IllegalStateException {
		if(rootPath == null) throw new NullPointerException(TAG + "changeRoot(String rootPath) - root == null");
		setCurrentDir(new File(rootPath));
		listCurrentDir();
	}
	
}
